package top.jle.jpa.basic.dao;

import com.github.starnowski.posjsonhelper.hibernate6.JsonBExtractPathText;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.Validate;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.type.descriptor.java.LongJavaType;
import org.hibernate.type.descriptor.jdbc.BigIntJdbcType;
import org.hibernate.type.internal.BasicTypeImpl;
import top.jle.jpa.basic.dao.PropertyFilter.MatchType;

import static java.util.Collections.singletonList;

@Getter
@Setter
public final class JSONPropertyFilter implements PreFilter{

    private MatchType matchType;
    private Object matchValue;
    private String propertyName;
    private String subPropertyName;

    public JSONPropertyFilter(String propertyName,String subPropertyName,MatchType matchType, Object matchValue) {
        this.matchType = matchType;
        this.matchValue = matchValue;
        this.propertyName = propertyName;
        this.subPropertyName = subPropertyName;
    }

    @SuppressWarnings({"rawtypes"})
    private static <T> Predicate buildCriterion(Root<T> root,HibernateCriteriaBuilder detachedCriteria,final String aliasPropertyName, final String subPropertyName,final MatchType matchType, final Object propertyValue) {
        Validate.notBlank(aliasPropertyName, "propertyName can't be null");
        Validate.notBlank(subPropertyName, "propertyName can't be null");
        // 根据MatchType构造criterion
        Predicate criterion = null;
        //构造属性
        String[] propertyPath = aliasPropertyName.split("\\.");
        Path e = root.get(propertyPath[0]);
        if(propertyPath.length>1){
            //构造关联表属性  eg. user.contactInfo.email
            for (int i = 1; i < propertyPath.length; i++) {
                e = e.get(propertyPath[i]);
            }
        }

        JsonBExtractPathText ex = new JsonBExtractPathText(e, singletonList(subPropertyName), (NodeBuilder) detachedCriteria);
        switch (matchType) {
            case EQ -> criterion = detachedCriteria.equal(ex, propertyValue);
            case LIKE -> criterion = detachedCriteria.like(ex, "%" + propertyValue + "%");
            case LESS_THAN -> criterion = detachedCriteria.lessThan(ex.castAs(new BasicTypeImpl<>(new LongJavaType(), new BigIntJdbcType())),(Long)propertyValue);
            case GREATER_THAN -> criterion = detachedCriteria.greaterThan(ex.castAs(new BasicTypeImpl<>(new LongJavaType(), new BigIntJdbcType())),(Long)propertyValue);
            case LESS_THAN_OR_EQ -> criterion = detachedCriteria.lessThanOrEqualTo(ex.castAs(new BasicTypeImpl<>(new LongJavaType(), new BigIntJdbcType())),(Long)propertyValue);
            case GREATER_THAN_OR_EQ -> criterion = detachedCriteria.greaterThanOrEqualTo(ex.castAs(new BasicTypeImpl<>(new LongJavaType(), new BigIntJdbcType())),(Long)propertyValue);
            case NOT_EQ -> criterion = detachedCriteria.notEqual(ex, propertyValue);
            case NOT_LIKE -> criterion = detachedCriteria.notLike(ex,"%" + propertyValue + "%");
            case START_WITH -> criterion = detachedCriteria.like(ex,propertyValue + "%");
            case END_WITH -> criterion = detachedCriteria.like(ex,"%"+propertyValue);
            case IS_NULL -> criterion = detachedCriteria.isNull(ex);
            case IS_NOT_NULL -> criterion = detachedCriteria.isNotNull(ex);
        }
        return criterion;
    }


    @Override
    public <T> Predicate buildCriterion(Root<T> root, HibernateCriteriaBuilder detachedCriteria) {

        return buildCriterion(root,detachedCriteria,this.getPropertyName(),getSubPropertyName(), this.getMatchType(), this.getMatchValue());
    }
}
